<?php
/**
 * Base class to operate database
 */
class Oa_Util_Db_Base
{
    private  $_dbRead;
    private  $_dbWrite;
    
    private $_dbName;
    private $_dbUser;
    private $_dbPwd;
    private $_dbReadHost;
    private $_dbWriteHost;
    private $_dbReadPort;
    private $_dbWritePort;
    private $_res;
    
    protected static $_readOnMaster = FALSE;

    const ERR       = -1;
    const FETCH_ALL = 0;
    const FETCH_ONE = 1;
    
    protected function __construct($dbName, $dbUser, $dbPwd, $dbReadHost, $dbReadPort, $dbWriteHost, $dbWritePort)
    {
        $this->_dbName      = $dbName;
        $this->_dbUser      = $dbUser;
        $this->_dbPwd       = $dbPwd;
        $this->_dbReadHost  = $dbReadHost;
        $this->_dbReadPort  = $dbReadPort;
        $this->_dbWriteHost = $dbWriteHost;
        $this->_dbWritePort = $dbWritePort;
    }

    public static function & getInstance($dbName, $dbUser, $dbPwd, $dbReadHost, $dbReadPort, $dbWriteHost, $dbWritePort)
    {
        $obj = new Oa_Util_Db_Base($dbName, $dbUser, $dbPwd, $dbReadHost, $dbReadPort, $dbWriteHost, $dbWritePort);
        return $obj;
    }

    public function setReadOnMaster($operate = TRUE)
    {
        self::$_readOnMaster = $operate;
    }
    
    /**
     * 获取SQL查询结果
     *
     * @param string  $sql
     * @param array   $res Out parameter, array to be filled with fetched results
     * @param integer $fetchStyle 获取命名列或是数字索引列，默认为命令列
     * @param integer $fetchMode  获取全部或是一行，默认获取全部
     * @return boolean|integer false on failure, else return count of fetched rows
     */
    public function select($sql, &$res, $fetchStyle = PDO::FETCH_NAMED, $fetchMode = self::FETCH_ALL)
    {
        try {
            if (self::$_readOnMaster) {
                $db = &$this->getDbWrite();
            } else {
            	$db = &$this->getDbRead();
            }
            
            $this->_res = $db->query($sql);
            
            if ($this->_res === FALSE) {
                //Log error here
                return FALSE;
            }

            if ($fetchMode === self::FETCH_ALL) {
                $res = $this->_res->fetchAll($fetchStyle);
                return count($res);
            } else if ($fetchMode === self::FETCH_ONE) {
                $res = $this->_res->fetch($fetchStyle);
                //自动关闭Cursor，以便在同一Statement对象执行下一条语句
                $this->_res->closeCursor(); 
                return $res ? 1 : 0;
            } else {
                return FALSE;
            }
        } catch (PDOException $e) {
            //Log err here
            return FALSE;
        }
    }

    /**
     *  获取查询结果下一行
     *  
     *  @param array $res Out parameter, array to be filled with fetched results
     *  @param integer $fetchStyle same as select method
     *  @return boolean false on failure, true on success
     */
    public function fetchNext(&$res, $fetchStyle = PDO::FETCH_NAMED)
    {
        if (!empty($this->_res)) {
            try {
                $res = $this->_res->fetch($fetchStyle);
            } catch (Exception $e) {
                //Log error here
                return FALSE;
            }
            return TRUE;
        }

        return FALSE;
    }

    /**
     * update/delete/insert/replace sql use this method
     *
     * @param string $sql sql语句
     * @param string $mode if is 'a', return affected rows count, else return boolean
     * @return boolean|integer 
     */
    public function mod($sql, $mode = '')
    {
        try {
            $db  = &$this->getDbWrite();
            $res =  $db->exec($sql);
            if ($res === FALSE) {
                //Log error here
                return FALSE;
            }
        } catch (PDOException $e) {
            //Log error here
            return FALSE;
        }

        if ($mode == 'a') {
            return $res;
        }

        return TRUE;
    }

    public function & getDbWrite()
    {
        try {
            if (!$this->_dbWrite) {
                $dsn = 'mysql:host=' . $this->_dbWriteHost . ';port='
                     . $this->_dbWritePort . ';dbname=' . $this->_dbName;

                $this->_dbWrite =
                    new PDO($dsn, $this->_dbUser, $this->_dbPwd, array(
                            //PDO::ATTR_PERSISTENT => TRUE,
                            //PDO::MYSQL_ATTR_DIRECT_QUERY => TRUE,
                            PDO::ATTR_EMULATE_PREPARES => TRUE,
                            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'',
                        )
                    );
            }
            return $this->_dbWrite;
        } catch (PDOException $e) {
            //Log error here
            return FALSE;
        }
    }

    public function & getDbRead()
    {
        try {
            if (!$this->_dbRead) {
                $dsn = 'mysql:host=' . $this->_dbReadHost . ';port='
                     . $this->_dbReadPort . ';dbname=' . $this->_dbName;

                $this->_dbRead = 
                    new PDO($dsn, $this->_dbUser, $this->_dbPwd, array(
                            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
                            PDO::ATTR_EMULATE_PREPARES => TRUE,
                            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'',
                        )
                    );
            }
            return $this->_dbRead;
        } catch (PDOException $e) {
            //Log error here
            return FALSE;
        }
    }

    /**
     * 获取上次insert操作时得到的自增id
     *
     * @return integer
     */
    public function getLastId()
    {
    	if ($this->_dbWrite) {
            return $this->_dbWrite->lastInsertId();
    	}
    	return 0;
    }

    /**
     * 获取sql读取错误信息
     *
     * @return string
     */
    public function getReadErrorInfo()
    {
        if (!self::$_readOnMaster) {
            $db = $this->_dbRead;
        } else {
            $db = $this->_dbWrite;
        }

        if (!empty($db)) {
            $err = $db->errorInfo();
            return $err[2];
        }

        return "Db Reader Not initiated\n";
    }

    /**
     * 获取sql写入错误信息
     *
     * @return string
     */
    public function getWriteErrorInfo()
    {
        if (!empty($this->_dbWrite)) {
            $err = $this->_dbWrite->errorInfo();
            return $err[2];
        }

        return "DB Writer not initiated\n";
    }
    
    /**
     * 判断上次错误是否由于重复key引起
     *
     * @return boolean
     */
    public function isDuplicate()
    {
        if (!empty($this->_dbWrite)) {
            $err = $this->_dbWrite->errorInfo();
            return $err[1] == 1062;
        }

        return FALSE;
    }

    public function close()
    {
        if ($this->_dbWrite) {
            $this->_dbWrite = NULL;
        }
        if ($this->_dbRead) {
            $this->_dbRead = NULL;
        }
    }
}
